#include <VirtualWire.h>
#include <EEPROM.h>

/*
  RF_315_RX_light
  
  Memorizza l'indirizzo nella memoria non volatile.
  1 bottone e 1 led.
  SEQUENZA ACCENSIONE: 2 lampeggi veloci + lampeggia tante volte quanto è il numero memorizzato (3 default)
  
  1) Alla prima pressione la luce viene spenta (SPEGNIMENTO MANUALE)
  
  2) Con il doppio click la luce viene accesa (ACCENSIONE MANUALE)
  
  2) Premendo il tasto a lungo si entra in programmazione
   - due lampeggi voloci
   - si preme il tasto il numero di volte da memeorizzare
   - quando non si preme più per un po' di tempo il dato viene memorizzato -> ripete SEQUENZA ACCENSIONE
   - se, una volta entrati in programmazione, non si preme -> viene lasciato il valore precedente
   
  RICEZIONE DI UN MESSAGGIO: verfica l'indirizzo richiesto è compatibile con il proprio e se del tipo "light"
  - accende e spegne a seconda del comando
    *1*0*ADDRES## -> spegne
    *1*1*ADDRES## -> accende
 */

 /////////////////
 //#define DEBUG 
 /////////////////
 
 #ifdef DEBUG
  #define DEBUG_PRINT(x)  Serial.print (x)
  #define DEBUG_PRINTLN(x)  Serial.println (x)
#else
  #define DEBUG_PRINT(x)
  #define DEBUG_PRINTLN(x)
#endif
 
 const int INTERNAL_LED_PIN = 13;
 
 const int LED_PIN = 6;
 const int BUTTON_PIN = 2;
 
 const int EEPROM_ADDRESS = 50;
 
 const unsigned long LONG_TIME_PRESSION_MILLIS = 3000UL;
 const unsigned long EXIT_PROGRAMMING_MILLIS = 6000UL;
 const unsigned long DOUBLE_PRESSION_MILLIS = 300UL;
 

 const unsigned int MAX_VAL = 9;
 const unsigned int DEFAULT_VAL = 3;

 //Button state
 unsigned int button_prev = LOW; 
 unsigned long button_first_time_pression_millis = 0;
 unsigned long button_long_time_pression_millis = 0;

 boolean state_programming = false;
 unsigned long last_programming_event_millis = 0;

 unsigned int val = DEFAULT_VAL;
 unsigned int new_val = 0;
 
 void setup() 
 { 
   #ifdef DEBUG
    Serial.begin(9600);	// Debugging only
   #endif
   DEBUG_PRINTLN("SETUP");   
   
   pinMode(INTERNAL_LED_PIN, OUTPUT);
   pinMode(LED_PIN, OUTPUT);
   
   pinMode(BUTTON_PIN, INPUT);
   
   val = EEPROM.read(EEPROM_ADDRESS);
   if(val>MAX_VAL)
     val = DEFAULT_VAL;
       
   fastBlink();
   delay(250);
   blinkForValue();
   
   setupRX();
 }

 void loop() 
 {
  unsigned long current_time_millis = millis();
  
  int button = digitalRead(BUTTON_PIN);
  
  if ((button == LOW) && (button_prev == LOW) && !state_programming)
  {
    checkRX();
  }
  
  if ((button == HIGH) && (button_prev == LOW)) //Tasto premuto per la prima volta
  {
    
    if(!state_programming)
    {
      //Verifico il doppio click per decidere se spegnere o accendere la luce (MANUALE)
      if(current_time_millis-button_first_time_pression_millis < DOUBLE_PRESSION_MILLIS) //doppio click
        lightOn();
      else
        lightOff();
    }
    
    button_first_time_pression_millis = current_time_millis;
    if(state_programming)
    {
      last_programming_event_millis = current_time_millis;
      singleBlink();
      new_val++;
    }
    delay(200);
  }
  
  if(!state_programming) //Verifico le condizioni per entrare in programmazione
  {
    if ((button == HIGH) && (button_prev == HIGH)) //Tasto premuto ancora
    {
      if(current_time_millis-button_first_time_pression_millis > LONG_TIME_PRESSION_MILLIS) //long time pression millis
      {
        state_programming = true;
        stopRX();
        fastBlink();
        new_val = 0;
        last_programming_event_millis = current_time_millis;
        //delay(100);
      }
    }
  }
  else 
  {
    //Verifico le condizioni per uscire dalla programmazione
    if(button==LOW) //!!!!!!!!!!!!!!
    {
      if(current_time_millis-last_programming_event_millis>EXIT_PROGRAMMING_MILLIS) //non premo il tasto da un tot
      {
         state_programming = false;
         startRX();
         fastBlink();
         delay(250);  
         if(new_val>0)
         {
           if(val!=new_val)
           {
             val = new_val;
             EEPROM.write(EEPROM_ADDRESS, val);
           }
         }
         blinkForValue();
         //delay(250);         
      }
    }
    
  }
 
  //Aggiorno il valore precedente 
  button_prev = button;
}

 void singleBlink()
 {
    digitalWrite(LED_PIN, HIGH);
    delay(50);
    digitalWrite(LED_PIN, LOW);   
 }

 void fastBlink()
 {
   for(int i = 0; i <2; i++)
   {
     digitalWrite(LED_PIN, HIGH);
     delay(100);
     digitalWrite(LED_PIN, LOW);
     delay(100);
   } 
 }

 void blinkForValue()
 {
  digitalWrite(LED_PIN, LOW);
  delay(100);
  
  for(int i = 0; i <val; i++)
  {
    digitalWrite(LED_PIN, HIGH);
    delay(250);
    digitalWrite(LED_PIN, LOW);
    delay(250);
  } 
 }

 void setupRX()
 {
   // Initialise the IO and ISR
    vw_set_ptt_inverted(true); // Required for DR3100
    vw_setup(2000);	 // Bits per sec

    vw_rx_start();       // Start the receiver PLL running
 }

 void startRX()
 {
   vw_rx_start();
 }

 void stopRX()
 {
   vw_rx_stop();
 }


 void checkRX()
 {
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;

    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
      
      DEBUG_PRINT("Recived: ");
      for (int i = 0; i < buflen; i++)
      {
         DEBUG_PRINT((char)buf[i]);
      }
      DEBUG_PRINTLN("");     
      
      digitalWrite(INTERNAL_LED_PIN, HIGH); // Flash a light to show received good message
      //delay(50);
      //digitalWrite(INTERNAL_LED, LOW); 
      int i;
        
      //Mi aspetto un command -> inizia con * non seguito da #
      //*WHO*WHATH*WHERE##
      if((char)buf[0]=='*' && (char)buf[1]!='#')
      {
        char who = (char)buf[1]; //WHO è lungo 1 carattere
        unsigned int lenWho = 1;
        char* what = NULL;
        unsigned int lenWhat = 0;
        char* where = NULL;
        unsigned int lenWhere = 0;
        
        //WHAT e WHERE possono essere più lunghi -> sono char*
        if((char)buf[2]=='*')
        {
          what = (char*)&buf[3];
          for (i = 3; i < buflen; i++)
          {
            if((char)buf[i]=='*')
            {
              where = (char*)&buf[i+1];
              for(int j=i+1;j<buflen; j++)
              {
                if((char)buf[j]=='#' && (char)buf[j+1]=='#')
                {
                  break;
                }
                else
                {
                  lenWhere++;
                }
              }  
              break;
            }  
            else
            {
              lenWhat ++;
            }
          }
        }
          
      DEBUG_PRINT("WHO: ");
      DEBUG_PRINT(who);      
      DEBUG_PRINT(" WHAT: ");
      for (int i = 0; i < lenWhat; i++)
      {
         DEBUG_PRINT((char)what[i]);
      }
      DEBUG_PRINT(" WHERE: ");
      for (int i = 0; i < lenWhere; i++)
      {
         DEBUG_PRINT((char)where[i]);
      }

      DEBUG_PRINTLN("");         
         
        //Eseguo il comando
        if(who=='1') //Gestione luci
        {
          unsigned int uiWhere = *where - '0';
          if(uiWhere==val)
          {
            if(*what=='1')
              lightOn();
            else if (*what=='0')
              lightOff();
          }
        }   
      }
      digitalWrite(INTERNAL_LED_PIN, LOW);
    }
 }
 
 void lightOn()
 {
   digitalWrite(LED_PIN, HIGH);
 }

 void lightOff()
 {
   digitalWrite(LED_PIN, LOW);
 }
 
 

